package com.base.dynamicprograming;

import java.util.stream.Stream;

/**
 * @ClassName: MinCostClimbingStairs
 * @Description: 746. 使用最小花费爬楼梯
 *数组的每个下标作为一个阶梯，第 i 个阶梯对应着一个非负数的体力花费值 cost[i]（下标从 0 开始）。
 *
 * 每当你爬上一个阶梯你都要花费对应的体力值，一旦支付了相应的体力值，你就可以选择向上爬一个阶梯或者爬两个阶梯。
 *
 * 请你找出达到楼层顶部的最低花费。在开始时，你可以选择从下标为 0 或 1 的元素作为初始阶梯
 *
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/min-cost-climbing-stairs
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 *
 * @author: li
 * @Date: 2021/8/20 6:37 下午
 */
public class MinCostClimbingStairs {
    public int minCostClimbingStairs(int[] costs){
        int n =costs.length;
        //dp[i]代表到达第i个底层的最小花费（也就是第i+1层）
        //层数 0 1 2 3 4 5
        //顶楼 1 2 3 4 5 6
        // 在到底顶楼3之前，上一步可能在顶楼2，所以层2要加入，然后在麦一步就顶楼3，可能在顶楼1，所以层1要加入
        // 推到公式为： 顶楼3 = min(顶楼2+2层，顶楼1+1层)
        int[] dp = new int[n+1];
        // 当costs只有一个元素时，代表第0个阶梯需要的花费，所以我们可以一次两步，直接到楼顶，所以不需要花费。
        dp[0] = dp[1] = 0;
        for (int i = 2; i <= n; i++) {
            dp[i] = Math.min(dp[i-1]+costs[i-1],dp[i-2]+costs[i-2]);
        }
        return dp[n];
    }

    public int minCostClimbingStairs1(int[] cost) {
        int n = cost.length;
        int pre = 0,q = 0;
        for (int i = 2; i <= n; i++) {
            int tmp = Math.min(q + cost[i - 1], pre + cost[i - 2]);
            pre = q;
            q = tmp;
        }
        return q;
    }

    /**
     * 通过求解到达n-1,n-2楼梯的花费，则到底楼顶n = min(  f(n-1),  cost[n]+f(n-2) )
     * @param cost
     * @return
     */
    public int minCostClimbingStairs2(int[] cost) {

        if(cost.length<2){
            return 0;
        }
        if(cost.length==2){
            return Math.min(cost[0],cost[1]);
        }
        int pre = cost[0],q = cost[1];
        for(int i = 2;i<cost.length-1;i++){
            int tmp =  cost[i] + Math.min(pre,q);
            pre = q;
            q = tmp;
        }
        return Math.min(pre+cost[cost.length-1],q);

    }
}
